
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;

using Swaf;
using Swaf.BizObj;
using Swaf.BizRule;
using Swaf.Container;
using Swaf.Security;

using ICSharpCode.SharpZipLib.Zip.Compression;
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
using ICSharpCode.SharpZipLib.GZip;
using ICSharpCode.SharpZipLib.Zip;
using ICSharpCode.SharpZipLib.Checksums;
using ICSharpCode.SharpZipLib.Core;

namespace eCom
{
	class eComSignUpBizRules : Syrinx.BaseBizRules
	{
		protected static Regex s_yellowPageProductKey = new Regex("^yp-(.*)", RegexOptions.IgnoreCase);
		protected static Regex s_newUserSignupProductKey = new Regex("^um-(.*)", RegexOptions.IgnoreCase);
		protected static Regex s_newCommunityChildCompanyProductKey = 
			new Regex("^ccm-([^-]+)-([^-]+)-([^-]+)-([^-]+)-([^-]+)-([^-]+)-([^-]+)-([^-]+)", RegexOptions.IgnoreCase);

		public SignUpResult processNewYellowPageEntrySignUp(IApplication app, IRuleContext context, IBizObj newYellowPageAd, IBizObj signUp)
		{
			SignUpResult sur = new SignUpResult();
			sur.EComSignUpResultInfo = eComSignUpResult.InvalidProductKey;
			if (signUp != null)
			{
				Match m = s_yellowPageProductKey.Match((string)signUp.get("productKey"));
				if (m.Success)
				{
					string yellowPageLevel = m.Groups[1].Value;
				}
			}
			return sur;
		}

		public IBizObjList getCreateNewUserStyles(IApplication app, IRuleContext context)
		{
			return app.appData.get("eComUserMembershipCreationTypes") as IBizObjList;
		}

		public SignUpResult processNewUserSignUp(IApplication app, IRuleContext context, IBizObj signUp, Uri usersFirstUrlOnSite)
		{
			try
			{
				CreateNewUserStyle createType = CreateNewUserStyle.createAndLogin;
				
				SignUpResult sur = new SignUpResult();
				sur.EComSignUpResultInfo = eComSignUpResult.InvalidProductKey;
				if (signUp != null)
				{
				
					string key = signUp.get("productKey").ToString();
					if (key == null || !key.Contains("-"))
						return sur;

					string[] parts = key.Split('-');
					string membership = parts[0];
					if (!membership.StartsWith("UM",true,null))
						return sur;
					if (membership.Length == 2)
						membership = (string)ActiveCompany.get("settings.defaultCreateNewUser");
					else
						membership = membership.Substring(2);

					if (!Cmn.IsEmpty(membership))
						createType = (CreateNewUserStyle)Enum.Parse(typeof(CreateNewUserStyle), membership, true);

					IResults rc = null;
					IBizObj user = signUp.get("rootUserInfo") as IBizObj;
					user.put("id", new Swaf.BGuid.Guid());

					context.put("companyId", ActiveCompanyId);
					string userGroup = parts[1];
					context.put("name", userGroup);
					IBizObj obj = app.appData.get("DA.eCom.GetUserGroupByName",context) as IBizObj;
					if (obj != null)
					{
						rc = app.ruleMgr.execute(context,"getUserGroupDetails",obj.get("id") as Swaf.BGuid.Guid);
						if (rc.ExceptionInfo == null && rc.CallResults is IBizObj)
							((IBizObjList)user.get("groups")).Add(rc.CallResults);
					}

					if (createType == CreateNewUserStyle.createCalculated)
					{
						//TODO: Call rule to generate userid
						//TODO: Call another rule to generate the password.
					}

					Swaf.BGuid.Guid contactId = signUp.get("contactInfo.id") as Swaf.BGuid.Guid;
					if (contactId == null)
						contactId = new Swaf.BGuid.Guid();

					user.put("id", contactId);
					user.put("companyId",ActiveCompanyId);
					user.put("enable", createType != CreateNewUserStyle.createDisabled);
					rc = app.ruleMgr.execute("setupUserProfile", user);
					if (rc.ExceptionInfo == null)
					{
						if (parts.Length > 2)
						{
							user.put("limits.id", user.get("id"));

							bool canEnableBlog = true;
							if (string.Compare(parts[2], "nb", true) == 0)
								canEnableBlog = false;
							user.put("limits.canEnableBlog", canEnableBlog);

							app.appData.put("DA.UserManagement.UpdateUserLimits", user.get("limits"));
						}

						if (signUp.get("salesOrderItemId") != null)
						{
							context.put("id", signUp.get("salesOrderItemId"));
							context.put("desc", string.Format("New User {0}", user.get("userName")));
							app.appData.put("DA.eCom.SetSalesOrderItemDescription", context);
						}
						sur.EComSignUpResultInfo = eComSignUpResult.Success;

						if(createType == CreateNewUserStyle.createAndLogin)
							app.ruleMgr.execute("loginUser", user);


						//Use the event notification bizrule to send emails about new user membership being created.
						//This can be very important for the disabled user created and auto user created options 
						//referenced below because the new user needs an email sent to them with information about how
						//to use their new account.
						FlexiMap m = new FlexiMap();
						if (createType == CreateNewUserStyle.createDisabled)
						{
							IBizObj emailVerification = app.bizObjMgr.create("UserEmailVerification");
							emailVerification.put("id", new Swaf.BGuid.Guid());
							emailVerification.put("completeBizRule", "activateUserSignup");
							emailVerification.put("activateData", null);
							rc = context.RuleGroup.execute(context, "createUserEmailVerification", emailVerification);
							m.put("emailValidationUrlParams", rc.CallResults);
						}

						//m.put("item", item);
						rc = context.RuleGroup.execute("notifyObservers", null, "",
							new object[] { "ecomUserCreated_" + ActiveCompany.get("settings.defaultCreateNewUser"), m });

						string eventName = "ecomNewUserCreated";
						if (createType == CreateNewUserStyle.createCalculated)
							eventName = "ecomNewDisabledUserCreated";
						else
							eventName = "ecomNewAutoUserCreated";

						context.RuleGroup.execute("notifyObservers", null, "", new object[] {
						eventName, signUp});

					}
					else
						sur.EComSignUpResultInfo = eComSignUpResult.InvalidNewUserAccount;
				}
				return sur;
			}
			catch (Exception e)
			{
				throw new eComException("error executing processNewUserSignUp.", e);
			}
		}
		
		public SignUpResult processNewChildCompanySignUp(IApplication app, IRuleContext context, IBizObj signUp)
		{
			try
			{
				if (signUp != null)
				{
					SignUpResult sur = new SignUpResult();
					//PK1: PRODUCTCODE-TEMPLATECOMPANYSHORTNAME-THEME-MAXCHILDCOMPANY-MAXUSER-MAXCHILDCOMPANYUSER-MAXARTICLES-MAXIMAGES-EXTRA
					if (false.Equals(ActiveCompany.get("canCreateChildCompanies")))
					{
						sur.EComSignUpResultInfo = eComSignUpResult.ParentCannotCreateChildCompany;
						return sur;
					}
					
					string key = signUp.get("productKey").ToString();
					if (key == null || !key.Contains("-"))
					{
						sur.EComSignUpResultInfo = eComSignUpResult.InvalidProductKey;
						return sur;
					}
					string[] pk = signUp.get("productKey").ToString().Split('-');
					if (pk.Length < 8)
					{
						sur.EComSignUpResultInfo = eComSignUpResult.InvalidProductKey;
						return sur;
					}

					string pc = pk[0].Trim().ToUpper();
					string templateCompanyShortName = pk[1];
					string themeName = pk[2];
					int cc = Convert.ToInt16(pk[3]);
					int mu = Convert.ToInt16(pk[4]);
					int mcc = Convert.ToInt16(pk[5]);
					int ma = Convert.ToInt16(pk[6]);
					int mi = Convert.ToInt16(pk[7]);

					if (pc != "CCM" && pc != "ECM")
					{
						sur.EComSignUpResultInfo = eComSignUpResult.InvalidProductKey;
						return sur;
					}

					IBizObj newCompany = signUp.get("companyInfo") as IBizObj;
					newCompany.put("id", new Swaf.BGuid.Guid());
					newCompany.put("canCreateChildCompanies", cc > 0 ? 1 : 0);
					newCompany.put("autoCreateUser", mu > 0 ? 1 : 0);
					newCompany.put("autoCreateCompany", mcc > 0 ? 1 : 0);
					newCompany.put("parentId", ActiveCompanyId);

					newCompany.put("settings.id", newCompany.get("id"));
					newCompany.put("settings.themeName", themeName);
					newCompany.put("settings.sitePath", "/" + newCompany.get("shortName"));
					newCompany.put("settings.maxUser", mu);
					newCompany.put("settings.maxChildCompany", cc);

					IBizObj limit = newCompany.get("limits") as IBizObj;
					limit.put("id", newCompany.get("id") as IBizObj);
					limit.put("maxTotalArticles", ma);
					limit.put("maxImages", mi);
					limit.put("maxImageSize", 1000);

					IResults rc = app.ruleMgr.execute("getCompanyByShortName", templateCompanyShortName);
					IBizObj templateCompany = rc.CallResults as IBizObj;
					if (rc.ExceptionInfo != null || templateCompany == null)
					{
						sur.EComSignUpResultInfo = eComSignUpResult.InvalidProductKey;
						return sur;
					}

					string root = app.resMgr.resolve("$ConfigParam1$");
					if (root.StartsWith("file://"))
						root = root.Substring(7);
					string zipfilename = Cmn.pathCombine(root,string.Format("{0}{1}{2}.zip"
						, app.resMgr.getMacro("BackupRestoreFileStore"), Path.DirectorySeparatorChar, templateCompanyShortName));

					//if (File.Exists(zipfilename))
					//{
					//    //TODO: call restore from this file
					//}
					//else
					//{
						//Debug.Assert(newAdminUser != null);
						//((IBizObjList)newAdminUser.get("groups")).Add(app.appData["userLoginOptions.defaultAdminUserGroup"] as IBizObj);

						//string anonu = string.Format("{0}anonymous", newCompany.get("shortName"));
						//IBizObj newAnonUser = app.bizObjMgr.create("User");
						//newAnonUser.put("userName", "anonymous");
						//newAnonUser.put("password", "anonpassword");
						//((IBizObjList)newAnonUser.get("groups")).Add(app.appData["userLoginOptions.defaultAnonymousUserGroup"] as IBizObj);

						IBizObj info = app.bizObjMgr.create("CompanyBackup");
						IBizObj tmpcmp = ((IBizObjList)info.get("backupCompanies")).insertNew();
						tmpcmp.put("shortName", templateCompanyShortName);
						tmpcmp.put("id", templateCompany.get("id"));
						tmpcmp.put("parentId", templateCompany.get("parentId"));
						info.put("fullCompanyBackup", true);
						IBizObj newAdminUser = signUp.get("rootUserInfo") as IBizObj;
						((IBizObjList)info.get("restoreAdditionalUsers")).Add(newAdminUser);

						using (FileStream fs = File.Create(zipfilename))
							using(ZipOutputStream zOut = new ZipOutputStream(fs))
								rc = app.ruleMgr.execute("executeCompanyBackup", info, zOut);

						info.put("filePath", zipfilename);

						((IBizObjList)info.get("restoreCompanies")).Add(newCompany);
						rc = app.ruleMgr.execute("executeCompanyRestore", info);
						if (rc.ExceptionInfo != null)
							throw new eComException("error calling restoreBackupForCompany bizrule from processNewChildCompanySignUp.",
								rc.ExceptionInfo);
					//}

					context.put("id", signUp.get("salesOrderItemId"));
					context.put("desc", string.Format("New Site for {0}, accessable as [SiteName]/{1}", newCompany.get("name"), newCompany.get("shortName")));
					app.appData.put("DA.eCom.SetSalesOrderItemDescription", context);
					sur.EComSignUpResultInfo = eComSignUpResult.Success;

					context.RuleGroup.execute("notifyObservers", null, "", new object[] {
						"ecomNewCommunityCreated", signUp});

					return sur;
				}
				return null;
			}
			catch (Exception e)
			{
				throw new eComException("error executing processNewChildCompanySignUp.", e);

			}
		}
	}
	[Serializable]
	public class SignUpResult
	{
		protected eComSignUpResult m_signUpResult;
		public eComSignUpResult EComSignUpResultInfo
		{
			get { return m_signUpResult; }
			set { m_signUpResult = value; }
		}

	}

	public enum eComSignUpResult
	{
		Success,
		InvalidShortName,
		InvalidProductKey,
		ParentCannotCreateChildCompany,
		InvalidNewUserAccount
	}
	public enum CreateNewUserStyle
	{
		/// <summary>
		/// Will allow user to pick userid and password, create new enabled user account, and log them in during checkout
		/// </summary>
		createAndLogin,

		/// <summary>
		/// Will allow user to pick userid and password, create new disabled user account. 
		/// Notification should have email with embedded validation link.
		/// </summary>
		createDisabled,

		/// <summary>
		/// Will automatically pick a userid and password, create a new enabled account.  
		/// Notification should have email with embedded userid/password that was generated.
		/// </summary>
		createCalculated
	}

	public class UserSignupCheckoutIterator : IBizObjIterator
	{
		#region IBizObjIterator Members

		public void iterate(IRuleContext context, IBizObjList baseList, IBizObj item)
		{
			string partId = (string)item.get("product.partId");
			if (partId.ToLower().StartsWith("um"))
			{
				//if (defaultCreateNewUser == CreateNewUserStyle.createCalculated)
				//{
				//	context.RuleGroup.execute(context, "processNewUserSignUp", item, context.get("firstUrl"));
				//}
			}
		}

		public void postIteration(IRuleContext context, ref IBizObjList baseList)
		{
		}

		public void preIteration(IRuleContext context, ref IBizObjList baseList)
		{
		}

		#endregion
	}
}
